#!/bin/bash
#
# Author:   Dr. Mike Murphy (mmurphy2@coastal.edu)
# Revision: 11 November 2013
#
#   Copyright 2012-2013 Coastal Carolina University
#
#   Licensed under the Apache License, Version 2.0 (the "License");
#   you may not use this file except in compliance with the License.
#   You may obtain a copy of the License at
#
#       http://www.apache.org/licenses/LICENSE-2.0
#
#   Unless required by applicable law or agreed to in writing, software
#   distributed under the License is distributed on an "AS IS" BASIS,
#   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#   See the License for the specific language governing permissions and
#   limitations under the License.
#


# The VirtualBox additions and extension pack are on the mirror, in the
# subdirectory. The latest version file is in the root of the mirror.
declare -g P_AGENT_VIRTUALBOX_MIRROR="http://download.virtualbox.org/virtualbox"
declare -g P_AGENT_VIRTUALBOX_SUBDIRECTORY="%{version}"
declare -g P_AGENT_VIRTUALBOX_LATEST="LATEST.TXT"
declare -g P_AGENT_VIRTUALBOX_ADDITIONS="VBoxGuestAdditions_%{version}.iso"
declare -g P_AGENT_VIRTUALBOX_EXTPACK="Oracle_VM_VirtualBox_Extension_Pack-%{version}.vbox-extpack"
declare -g P_AGENT_VIRTUALBOX_VERSION="auto"


## p_agent_virtualbox_help
##
## Echoes help information for the VirtualBox agent. Returns 0.
##
function p_agent_virtualbox_help() {
	cat << EOF
Maintains the guest additions or host extension pack and kernel driver
modules for VirtualBox. If the platform/virtualized context item is set to
virtualbox, then the guest additions will be automatically installed and
updated as necessary. For correct detection of VirtualBox guest machines, the
virtualization probe MUST be enabled by the Pulley subscription.

Alternatively, if platform/virtualized is empty, and
agent/virtualbox/extension_pack is set to "true", this agent will install and
maintain the VirtualBox extension pack. If agent/virtualbox/kernel is set
to "true", and the vboxdrv module is not loaded, this agent will compile and
install the kernel modules using the init script provided with VirtualBox.

By default, the guest additions or extension pack will be downloaded
directly from Oracle's server (virtualbox.org). If these files should be
downloaded from another server, the following context values can be set,
with the special string %{version} replaced with the VirtualBox version:

agent/virtualbox/mirror
  Server path. Default: http://download.virtualbox.org/virtualbox

agent/virtualbox/subdirectory
  Subdirectory on the server containing the actual downloads.
  Default: %{version}

agent/virtualbox/latest
  File containing the latest VirtualBox version. This file is located
  directly under the mirror path (outside the subdirectory) on the server.
  Default: LATEST.TXT

agent/virtualbox/additions
  Name of the VirtualBox Guest Additions ISO file, located in the
  subdirectory on the mirror. Default: VBoxGuestAdditions_%{version}.iso

agent/virtualbox/extpack_name
  Name of the VirtualBox Extension Pack file, located in the subdirectory
  on the mirror.
  Default: Oracle_VM_VirtualBox_Extension_Pack-%{version}.vbox-extpack

agent/virtualbox/version
  Version of VirtualBox to use (substituted for %{version} in the various
  paths). If set to "auto", the VirtualBox agent will automatically select
  the latest version of the Guest Additions (if running as a guest) or the
  matching version of the Extension Pack for the copy of VirtualBox
  running on the host (if managing the host). Default: auto

NOTE TO SLACKWARE USERS: If VirtualBox is compiled with the SlackBuilds.org
build script, kernel module compilation is removed from the init script, and
this agent will be unable to compile modules automatically.

In check-only mode, this agent returns 100 if updates to the guest
additions or extension pack (as determined by context) are available, or if
vboxdrv is not loaded (if context is set).
EOF
	return 0
}


## p_agent_virtualbox_summary
##
## Echoes summary information for the VirtualBox agent. Returns 0.
##
function p_agent_virtualbox_summary() {
	echo "Maintains VirtualBox guest additions or host extensions"
	return 0
}


## p_agent_virtualbox_guest_version
##
## Determines the version of the VirtualBox guest additions that should be
## installed. Uses the agent/virtualbox/version context item if it is set;
## otherwise downloads and parses the latest version file from the server.
## Echoes the resulting version information. Returns 0 on success, non-zero
## if an error occurs.
##
function p_agent_virtualbox_guest_version() {
	local status=0
	local version=$(get_context agent/virtualbox/version)
	local mirror=$(get_context agent/virtualbox/mirror)
	local latest=$(get_context agent/virtualbox/latest)
	local tmpfile=
	
	if [[ -z "${version}" || "${version}" == "auto" ]]; then
		[ -z "${mirror}" ] && mirror="${P_AGENT_VIRTUALBOX_MIRROR}"
		[ -z "${latest}" ] && latest="${P_AGENT_VIRTUALBOX_LATEST}"
		
		tmpfile=$(p_download_temp "${mirror}/${latest}")
		status=$?
		
		if [[ ${status} -eq 0 ]]; then
			version=$(cat "${tmpfile}")
			status=$?
		fi
	fi
	
	[ -z "${version}" ] && status=1
	echo "${version}"
	return ${status}
}


## p_agent_virtualbox_check_guest
##
## Checks to see if the installed VirtualBox Guest Additions are at the
## latest available version. Returns 0 if the Guest Additions are up to
## date, or 100 if a new version of the Guest Additions is available (or if
## the Guest Additions are not installed).
##
function p_agent_virtualbox_check_guest() {
	local result=0
	local latest_version=$(p_agent_virtualbox_guest_version)
	local this_version=
	
	if [ -n "${latest_version}" ]; then
		which VBoxControl > /dev/null 2>&1
		if [ $? -eq 0 ]; then
			this_version=$(VBoxControl -v | sed s/r.*//)
			
			[ "${this_version}" != "${latest_version}" ] && result=100
		else
			# VirtualBox guest additions not yet installed
			result=100
		fi
	fi
	return ${result}
}


## p_agent_virtualbox_install_additions
##
## Installs the VirtualBox Guest Additions, using the latest version as
## determined by context and/or obtaining the information from the server.
## Returns 0 if successful, non-zero otherwise.
##
function p_agent_virtualbox_install_additions() {
	local mirror=$(get_context agent/virtualbox/mirror)
	local subdir=$(get_context agent/virtualbox/subdirectory)
	local additions=$(get_context agent/virtualbox/additions)
	local latest_version=$(p_agent_virtualbox_guest_version)
	local status=$?
	local dl=
	local iso=
	local mtpt=
	local result=
	
	echo "Installing VirtualBox Guest Additions..."
	
	if [ ${status} -eq 0 ]; then
		[ -z "${mirror}" ] && mirror="${P_AGENT_VIRTUALBOX_MIRROR}"
		[ -z "${subdir}" ] && subdir="${P_AGENT_VIRTUALBOX_SUBDIRECTORY}"
		[ -z "${additions}" ] && additions="${P_AGENT_VIRTUALBOX_ADDITIONS}"
		
		dl="${mirror}/${subdir}/${additions}"
		dl=$(echo "${dl}" | sed s/'%{version}'/"${latest_version}"/g)
		
		iso=$(p_download_temp "${dl}")
		status=$?
		
		if [ ${status} -eq 0 ]; then
			mtpt=$(s_mktempdir)
			result=$(mount -o ro,loop "${iso}" "${mtpt}" 2>&1)
			status=$?
			
			if [ ${status} -eq 0 ]; then
				"${mtpt}/VBoxLinuxAdditions.run"
				status=$?
				
				umount "${mtpt}"
			fi
		fi
	fi
	
	if [[ ${status} -eq 0 ]]; then
		echo "Guest Additions installed successfully"
	else
		echo "Guest Additions installation failed"
	fi
	
	return ${status}
}


## p_agent_virtualbox_handle_guest <check | update>
##
## Checks the host to determine if the VirtualBox Guest Additions need to be
## installed or updated. If update is given as the argument, performs the
## Guest Additions installation or upgrade. Returns 0 if no updates are
## needed (check mode) or if updates complete successfully (update mode).
## Returns 100 if updates are available (check mode), or another non-zero
## value if an error occurs (either mode).
##
function p_agent_virtualbox_handle_guest() {
	local status=0
	
	p_agent_virtualbox_check_guest
	status=$?
	if [[ "$1" == "update" && ${status} -eq 100 ]]; then
		p_agent_virtualbox_install_additions
		status=$?
	fi
	
	return ${status}
}


## p_agent_virtualbox_host_version
##
## Echoes the version of the VirtualBox Extension Pack that should be
## installed on the host. If agent/virtualbox/version context is set, then
## this value specifies the version. Otherwise, the version is determined
## by the installed VirtualBox version.
##
## Returns 0 on success, non-zero if an error occurs.
##
function p_agent_virtualbox_host_version() {
	local version=$(get_context agent/virtualbox/version)
	local status=0
	
	if [[ -z "${version}" || "${version}" == "auto" ]]; then
		version=$(VBoxManage -v | sed s/r.*// | sed s/_OSE//)
		status=$?
	fi
	
	echo "${version}"
	
	return ${status}
}


## p_agent_virtualbox_check_host_driver
##
## Checks to see if the vboxdrv module is loaded on the host. Returns 0 if
## the module is loaded, 100 if the module is not loaded.
##
function p_agent_virtualbox_check_host_driver() {
	local status=0
	local mods=$(lsmod | grep vboxdrv)
	
	[ -n "${mods}" ] || status=100
	return ${status}
}


## p_agent_virtualbox_check_host_extpack
##
## Checks to see if the installed VirtualBox Extension Pack matches the
## version of VirtualBox installed on the system (or overridden using the
## agent/virtualbox/version context item). Returns 0 if no update to the
## Extension Pack is needed, 100 if a newer extension pack is available or
## no Extension Pack is installed yet, or another non-zero value if an
## error occurs.
##
function p_agent_virtualbox_check_host_extpack() {
	local version=$(p_agent_virtualbox_host_version)
	local status=$?
	local ext=
	local num=
	local ep_version=
	
	if [[ ${status} -eq 0 ]]; then
		ext=$(VBoxManage list extpacks | grep 'Oracle VM VirtualBox Extension Pack' 2>/dev/null)
		status=$?
		
		if [ -n "${ext}" ]; then
			num=$(VBoxManage list extpacks | awk '/Oracle VM VirtualBox Extension Pack/ {print NR + 1; exit 0; }')
			ep_version=$(VBoxManage list extpacks | tail -n +${num} | head -n 1 | awk '{print $2}')
			
			if [[ "${version}" != "${ep_version}" ]]; then
				status=100
			fi
		else
			status=100
		fi
	fi
	
	return ${status}
}


## p_agent_virtualbox_build_host_modules
##
## Builds host driver modules for VirtualBox, using the init script
## included with VirtualBox. Returns 0 on success, non-zero on error.
##
function p_agent_virtualbox_build_host_modules() {
	local result=$(/etc/init.d/vboxdrv setup 2>&1)
	local status=$?
	
	return ${status}
}


## p_agent_virtualbox_install_extpack
##
## Installs the VirtualBox Extension Pack from the server, using context
## entries and/or defaults to locate the proper Extension Pack. Returns 0
## upon success, non-zero otherwise.
##
function p_agent_virtualbox_install_extpack() {
	local version=$(p_agent_virtualbox_host_version)
	local status=$?
	local mirror=$(get_context agent/virtualbox/mirror)
	local subdir=$(get_context agent/virtualbox/subdirectory)
	local extpack=$(get_context agent/virtualbox/extpack_name)
	local dl=
	local outfile=
	local tempdir=
	
	if [ ${status} -eq 0 ]; then
		[ -z "${mirror}" ] && mirror="${P_AGENT_VIRTUALBOX_MIRROR}"
		[ -z "${subdir}" ] && subdir="${P_AGENT_VIRTUALBOX_SUBDIRECTORY}"
		[ -z "${extpack}" ] && extpack="${P_AGENT_VIRTUALBOX_EXTPACK}"
		
		dl="${mirror}/${subdir}/${extpack}"
		dl=$(echo "${dl}" | sed s/'%{version}'/"${version}"/g)
		outfile=$(basename "${dl}")
		
		tempdir=$(s_mktempdir)
		outfile="${tempdir}/${outfile}"
		
		p_download "${dl}" "${outfile}"
		status=$?
		if [[ ${status} -eq 0 ]]; then
			VBoxManage extpack install "${outfile}"
			status=$?
		fi
	fi
	
	return ${status}
}


## p_agent_virtualbox_handle_host <check | update>
##
## Checks the host to determine if VirtualBox drivers need to be compiled
## and/or if the Extension Pack needs to be updated. If update is supplied as
## the argument, performs updates as required. Returns 0 if no updates are
## needed (check mode) or if updates complete successfully (update mode).
## Returns 100 if updates are available (check mode), or another non-zero
## value if an error occurs (either mode).
##
function p_agent_virtualbox_handle_host() {
	local status=0
	local update_driver=$(get_context agent/virtualbox/kernel)
	local update_extpack=$(get_context agent/virtualbox/extension_pack)
	
	if [[ ${status} -eq 0 && "${update_driver}" == "true" ]]; then
		p_agent_virtualbox_check_host_driver
		status=$?
		if [[ "$1" == "update" && ${status} -eq 100 ]]; then
			p_agent_virtualbox_build_host_modules
			status=$?
		fi
	fi
	
	if [[ ${status} -eq 0 && "${update_extpack}" == "true" ]]; then
		p_agent_virtualbox_check_host_extpack
		status=$?
		if [[ "$1" == "update" && ${status} -eq 100 ]]; then
			p_agent_virtualbox_install_extpack
			status=$?
		fi
	fi
	
	return ${status}
}


## p_agent_virtualbox_check
##
## Checks to see if the VirtualBox guest additions (guest) or driver module
## and/or Extension Pack (host) require updates. Returns 0 if no updates are
## needed, 100 if updates are required, or another non-zero status on error.
##
function p_agent_virtualbox_check() {
	local os_virt=$(get_context platform/virtualized)
	local status=0
	if [[ -z "${os_virt}" ]]; then
		which VBoxManage > /dev/null 2> /dev/null
		if [[ $? -eq 0 ]]; then
			p_agent_virtualbox_handle_host check
			status=$?
		fi
	elif [[ "${os_virt}" == "virtualbox" ]]; then
		p_agent_virtualbox_handle_guest check
		status=$?
	fi
}


## p_agent_virtualbox_run
##
## Runs the VirtualBox agent, updating the host or guest as necessary.
## Returns 0 if successful, non-zero if an error occurs.
##
function p_agent_virtualbox_run() {
	local os_virt=$(get_context platform/virtualized)
	local status=0
	if [[ -z "${os_virt}" ]]; then
		which VBoxManage > /dev/null 2> /dev/null
		if [[ $? -eq 0 ]]; then
			p_agent_virtualbox_handle_host update
			status=$?
		fi
	elif [[ "${os_virt}" == "virtualbox" ]]; then
		p_agent_virtualbox_handle_guest update
		status=$?
	fi
}
